#include <pthread.h>
#include <signal.h>

#include "rotate_image.h"
#include "horizontal_flip_image.h"
#include "../libreria.h"
#include "../response_rotate_image.h"
#include "../response_horizontal_flip_image.h"

#define QUEUE_SIZE 5
#define NUM_SERVICE 2

char nome_servizio[NUM_SERVICE][BUFFER_SIZE];	// contiene i nomi dei servizi passati da riga di comando 
//List_Image * lista;				// lista di immagini

char ip_service[INET_ADDRSTRLEN] = "127.0.0.1";	// indirizzo IP di ascolto del Service Provider richieste del client
int porta_service; 				// porta di ascolto del Service Provider richieste del client

char ip_service_register[INET_ADDRSTRLEN];	// indirizzo IP del Service Register
int porta_service_register;			// porta del Service Register


void deregistra(int sig_receive)
{
	// Funzione che deregistra service_provider dal Registro dei Servizi
	// Va in esecuzione dopo la pressione del CTRL+C

	char msg[BUFFER_SIZE];
	memset(msg,0,BUFFER_SIZE);
	int num_service;
	int i;
	int s_deregistrazione;
	
	///////  CREAZIONE DEL SOCKET PER COMUNICARE COL SERVICE PROVIDER   //////
	s_deregistrazione=inizializza_client(ip_service_register, porta_service_register);	
	
	for (i=0; strcmp(nome_servizio[i],"#")!=0 && i<NUM_SERVICE; i++);
	num_service=i;

	// Invio messaggio del tipo "1;nome_servizio;porta_service;ip_service"
	sprintf(msg,"1;%d;%d;%s#",num_service,porta_service,ip_service);


	if(send(s_deregistrazione,msg,strlen(msg),0) == -1)
			printf("Errore comunicazione con Service Register\n");

	
	// Invio di un messaggio per ogni Servizio da deregistrare
	for (int i=0; i<num_service; i++)
	{
		
		memset(msg,0,BUFFER_SIZE);
		sprintf(msg,"%s;",nome_servizio[i]);
		if(send(s_deregistrazione,msg,strlen(msg),0) == -1)
			printf("Errore comunicazione con Service Register\n");

	}
	
	printf("Ho deregistrato tutti i servizi\n");

	close(s_deregistrazione);

	exit(1);
}


///////////////////// INIZIO THREAD ////////////////////////////////////////

void* gestisci_client(void *From)	
{
	
	host mitt;
	int i;
	int choice;

	mitt = *(host *)(From);//in mitt vanno i dati relativi al processo mittente
	char buffer [BUFFER_SIZE];
	
	////// ricevo stringa codificata in buffer /////

	memset(buffer,0,BUFFER_SIZE);
	i=0;

	do
		recv (mitt.s,&buffer[i],1,0);
	
	while (buffer[i++]!='#');

	///////////////////////////////////////////////
	
	char nome_servizio[BUFFER_SIZE];
	memset(nome_servizio,0,BUFFER_SIZE);
	i=0;
	while(buffer[i]!=';' && buffer[i]!='#')
	{
		nome_servizio[i]=buffer[i];
		i++;
	}	
	
	printf("Richiesta di servizio %s dal client (%s:%d).\n",nome_servizio,mitt.ip,mitt.porta);

	if(!strcmp("Rotate_Image",nome_servizio))
		choice=0;
	else if(!strcmp("Horizontal_Flip_Image",nome_servizio))
		choice=1;
	     else
		choice=-1;
	
	// In nome_servizio c'è il servizio effettivo che ha richiesto il client

	
	
	switch(choice)
	{
	case 0:{	

		//Servizio di Rotate_Image
		char nome_file[BUFFER_SIZE],lunghezza_file[BUFFER_SIZE];
		
		int len_file;
		char * dir=new char;

		Response_Rotate_Image rri(mitt);

		if(!rri.decode_request(&buffer[strlen(nome_servizio)+1],&nome_file[0],lunghezza_file, dir))
		{
			printf("Errore nella codifica dei parametri di Rotate_Image da parte del client (%s:%d),\n",mitt.ip,mitt.porta);
			rri.code_response(false,NULL);
			break;
		}

		len_file=atoi(lunghezza_file);

		if(len_file<=0)
		{
			printf("Errore nella codifica dei parametri di Rotate_Image da parte del client (%s:%d),\n",mitt.ip,mitt.porta);
			rri.code_response(false,NULL);
			break;
		}		
		
		//ricezione immagine da ruotare
		if(!rri.receive_image(nome_file,len_file))
		{		
			printf("Errore nella ricezione dell'immagine");
			rri.code_response(false,NULL);
			break;
		} 

		Rotate_Image ri (mitt);
		
		ri.Rotate_image(nome_file,dir); //rotazione immagine
		
		printf("Richiesta da client (%s:%d) di rotazione immagine %s effettuata\n",mitt.ip,mitt.porta,nome_file);
		
		if(!rri.code_response(true,nome_file))
		{		
			printf("Errore di comunicazione con il client\n");
			unlink(nome_file);
			break;
		} //invio lunghezza file ruotato


		// INVIO IMMAGINE RUOTATA

		if(!rri.send_image(nome_file))
			printf("Errore nell'invio dell'immagine ruotata a (%s:%d)\n",mitt.ip,mitt.porta); //invio immagine ruotata
		else
			printf("Richiesta da client (%s:%d) di invio immagine ruotata %s effettuata\n",mitt.ip,mitt.porta,nome_file);
		unlink(nome_file); //elimino file temporaneo
		
		
		}
		break;
	case 1:{
		
		//Servizio di Horizontal_Flip_Image

		char nome_file[BUFFER_SIZE],lunghezza_file[BUFFER_SIZE];
		int len_file;
		
		Horizontal_Flip_Image hfi(mitt);		

		Response_Horizontal_Flip_Image rhfi(mitt);
	
		if(!rhfi.decode_request(&buffer[strlen(nome_servizio)+1],&nome_file[0],lunghezza_file))
		{
			printf("Errore nella codifica dei parametri di Horizontal_Flip_Image da parte del client (%s:%d),\n",mitt.ip,mitt.porta);
			rhfi.code_response(false,NULL);
			break;
		}	
		len_file=atoi(lunghezza_file); // lunghezza del file da ricevere
		if(len_file<=0)
		{
			printf("Errore nella codifica dei parametri di Horizontal_Flip_Image da parte del client (%s:%d),\n",mitt.ip,mitt.porta);
			rhfi.code_response(false,NULL);
			break;
		}		
		
		rhfi.receive_image(nome_file,len_file); //ricezione immagine da ruotare
		
		hfi.Horizontal_Flip_image(nome_file); //rotazione immagine

		printf("Richiesta da client (%s:%d) di horizontal flip dell'immagine %s effettuata\n",mitt.ip,mitt.porta,nome_file);
		
		//invio lunghezza file ruotato		
		if(!rhfi.code_response(true,nome_file))
		{		
			printf("Errore di comunicazione con il client\n");
			unlink(nome_file);
			break;
		} 
		
		// INVIO IMMAGINE SPECCHIATA

		if(!rhfi.send_image(nome_file))
			printf("Errore nell'invio dell'immagine specchiata a (%s:%d)\n",mitt.ip,mitt.porta); //invio immagine ruotata
		else
			printf("Richiesta da (%s:%d) di invio immagine specchiata %s effettuata\n",mitt.ip,mitt.porta,nome_file);
		

		unlink(nome_file); //elimino file temporaneo

		
		}
		break;

	}

	close (mitt.s);
	return NULL;
}

int main (int argc, char * argv[])
{
	
	struct sockaddr_in client_addr;

	pthread_t tid;			//identificatore thread
	int i;
	int s;				//socket
	char msg [BUFFER_SIZE];
	int s_listening;    //socket di listening
	
	int s_accept;
	socklen_t len = sizeof(struct sock_addr*);//lunghezza in byte della struttura sock_add
	
	signal(SIGINT,deregistra);



	//Generazione porta random compresa tra 49152 e 65535

	srand(time(NULL));
	porta_service= rand()%16383;
	porta_service+=49152;
	///////////////////////////////////////////////////////

	if (argc > (3+NUM_SERVICE) || argc < 4)
	{
		printf("Errore numero parametri:\nUsage: ./ServiceProvider1 ip_service_register porta_service_register nome_servizio1 [nome_servizio2]\n");
		exit(1);
	}
	
	printf("SERVICE PROVIDER, v.1.0\n");

	
	porta_service_register = atoi(argv[2]);
	strcpy(ip_service_register,argv[1]);
	
	///// Inizializzazione socket per connessione al Service Register //////
	
	s=inizializza_client(ip_service_register,porta_service_register);
	
	//////////////////////////////////////////////////////////////////
	
	
	// REGISTRAZIONE DI SERVIZI SU SERVICE REGISTER //
	// < 0;num_servizi;nome_servizio;porta;ip>

	for(i=0; i<NUM_SERVICE;i++)
	
		strcpy(nome_servizio[i],"#");

	sprintf(msg,"0;%d;%d;%s#",(argc-3),porta_service,ip_service);
	
	if(send(s,msg,strlen(msg),0) == -1)
			printf("Errore comunicazione con Service Register\n");

	memset(msg,0,BUFFER_SIZE);

	for (int i=0; i<(argc-3); i++)
	{

		strcat(msg,argv[i+3]);
		strcat(msg,";");
		strcpy(nome_servizio[i],argv[i+3]);
		

	}
	if(send(s,msg,strlen(msg),0) == -1)
			printf("Errore comunicazione con Service Register\n");
	
	
	////////////////////////////////////////////////////

	
	

	///// Inizializzazione socket per ricezione richiesta da Client ///////	
	
	s_listening=inizializza_server(ip_service,porta_service,QUEUE_SIZE);

	printf ("Attesa di connessioni da client...\n");

	//lista= new List_Image();
	////////////////////////////////////////////////////////////////


	while(1)
	{

		//////////  ATTESA richieste da Client /////////////

		s_accept = accept(s_listening,(struct sockaddr*) &client_addr, &len);//creo il socket di connnessione
		if (s_accept == (-1))
		{ 
			printf ("ERRORE nella connessione!\n");
			exit(1);
		}
		
		host client;
		inet_ntop(AF_INET,&client_addr.sin_addr,client.ip,sizeof(client.ip));
		client.s = s_accept;
		client.porta = ntohs(client_addr.sin_port);

		//////// CREAZIONE di un thread per la gestione del Client /////////////
		if (pthread_create (&tid, NULL, gestisci_client,(void *)&client)!=0) 
		{
			printf("Errore nella creazione del Thread...\n");
			exit(1);
		}	

		memset(&client_addr,0,sizeof(client_addr));
	}
		
	

}


